Configルールを使ってパブリックアクセスが有効になっているRDSのDBインスタンスを自動修復する
はじめに
AWS Configはリソース構成を記録・管理するサービスで、Config ルールでは管理しているリソースの評価ができます。その条件に違反しているリソースは非準拠としてフラグをつけられます。
Config ルールにはこの違反した非準拠のリソースに対して「修復」のアクションを定義でき、想定したリソースの状態へと自動修復する機能があります。
今回は パブリックアクセス可能なRDSインスタンスを自動で修復する(パブリックアクセスを無効にする) Configルールを作成してみました。
CloudFormationテンプレート
StackSetsでマルチリージョン・マルチアカウントに展開できるよう、CloudFormationテンプレートで作成しました。
作成するリソース
- IAMロール
- 自動修復実行用
- Configルール
- 修復アクション
- SSMドキュメント
- AWS提供のドキュメントではなく新規に作成(理由は後ほど)
AWSTemplateFormatVersion: "2010-09-09" # ------------------------------------------------------------# # Resources # ------------------------------------------------------------# Resources: IamRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ssm-automation-role-for-rds-disable-public-access-${AWS::Region} Description: "role for ssm automation: s3 encryption" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "ssm.amazonaws.com" Action: "sts:AssumeRole" Policies: - PolicyName: !Sub Ssm-Automation-Policy-DisablePublicAccess-${AWS::Region} PolicyDocument: Version: "2012-10-17" Statement: Effect: "Allow" Action: - ssm:StartAutomationExecution - ssm:GetAutomationExecution - rds:DescribeDBInstances - rds:ModifyDBInstance Resource: "*" ConfigRule: Type: AWS::Config::ConfigRule Properties: ConfigRuleName: rds-instance-public-access-check Description: "Checks whether the Amazon Relational Database Service (RDS) instances are not publicly accessible. The rule is non-compliant if the publiclyAccessible field is true in the instance configuration item." Source: Owner: AWS SourceIdentifier: RDS_INSTANCE_PUBLIC_ACCESS_CHECK Scope: ComplianceResourceTypes: - "AWS::RDS::DBInstance" RemediationConfiguration: Type: AWS::Config::RemediationConfiguration Properties: Automatic: true ConfigRuleName: !Ref ConfigRule MaximumAutomaticAttempts: 5 RetryAttemptSeconds: 1200 TargetId: !Ref ConfigRemediationDisablePublicAccessToRDSInstance TargetType: "SSM_DOCUMENT" TargetVersion: "1" Parameters: DbiResourceId: ResourceValue: Value: RESOURCE_ID AutomationAssumeRole: StaticValue: Values: - !GetAtt [IamRole, "Arn"] ############################################################################################################ # RDSインスタンスのパブリックアクセス修正用SMSドキュメント # 当初以下AWS管理のドキュメントを使用予定だったが、新規作成のRDSインスタンスへの実行がエラーとなったため一部AWS管理のドキュメントを修正して利用 # https://docs.aws.amazon.com/systems-manager-automation-runbooks/latest/userguide/automation-aws-disable-rds-instance-public-access.html ############################################################################################################ ConfigRemediationDisablePublicAccessToRDSInstance: Type: AWS::SSM::Document Properties: DocumentType: Automation Name: ConfigRemediation-DisablePublicAccessToRDSInstance Content: schemaVersion: "0.3" description: | ### Document name - ConfigRemediation-DisablePublicAccessToRDSInstance ## What does this document do? The runbook disables public accessibility for the Amazon RDS database instance you specify using the [ModifyDBInstance](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_ModifyDBInstance.html) API. ## Input Parameters * AutomationAssumeRole: (Required) The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that allows Systems Manager Automation to perform the actions on your behalf. * DbiResourceId: (Required) The resource identifier for the DB instance you want to disable public accessibility. ## Output Parameters * DisablePubliclyAccessibleOnRDS.Response: The standard HTTP response from the ModifyDBInstance API. assumeRole: "{{ AutomationAssumeRole }}" parameters: AutomationAssumeRole: type: String description: (Required) The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that allows Systems Manager Automation to perform the actions on your behalf. allowedPattern: ^arn:(aws[a-zA-Z-]*)?:iam::\d{12}:role/[\w+=,.@-]+ DbiResourceId: type: String description: (Required) The resource identifier for the DB instance you want to disable public accessibility. allowedPattern: "db-[A-Z0-9]{26}" outputs: - DisablePubliclyAccessibleOnRDS.Response mainSteps: - name: GetRDSInstanceIdentifier action: "aws:executeAwsApi" description: | ## GetRDSInstanceIdentifier Gathers the DB instance identifier from the DB instance resource identifier. ## Outputs * DbInstanceIdentifier: The Amazon RDS DB instance identifier. timeoutSeconds: 600 isEnd: false inputs: Service: rds Api: DescribeDBInstances Filters: - Name: "dbi-resource-id" Values: - "{{ DbiResourceId }}" outputs: - Name: DbInstanceIdentifier Selector: $.DBInstances[0].DBInstanceIdentifier Type: String - name: WaitForDBInstanceStatusToAvailable action: "aws:waitForAwsResourceProperty" timeoutSeconds: 1200 isEnd: false description: | ## WaitForDBInstanceStatusToAvailable Waits for the DB instance to change to a AVAILABLE state. inputs: Service: rds Api: DescribeDBInstances DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PropertySelector: "$.DBInstances[0].DBInstanceStatus" DesiredValues: - "available" - name: VerifyDBInstanceStatus action: "aws:assertAwsResourceProperty" timeoutSeconds: 600 isEnd: false description: | ## VerifyDBInstanceStatus Verifies the DB instances is in an AVAILABLE state. inputs: Service: rds Api: DescribeDBInstances DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PropertySelector: "$.DBInstances[0].DBInstanceStatus" DesiredValues: - "available" - name: DisablePubliclyAccessibleOnRDS action: "aws:executeAwsApi" description: | ## DisablePubliclyAccessibleOnRDS Disables public accessibility on your DB instance. ## Outputs * Response: The standard HTTP response from the ModifyDBInstance API. timeoutSeconds: 600 isEnd: false inputs: Service: rds Api: ModifyDBInstance DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PubliclyAccessible: false outputs: - Name: Response Selector: $ Type: StringMap - name: WaitForDBInstanceStatusToModify action: "aws:waitForAwsResourceProperty" timeoutSeconds: 600 isEnd: false description: | ## WaitForDBInstanceStatusToModify Waits for the DB instance to change to a MODIFYING state. inputs: Service: rds Api: DescribeDBInstances DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PropertySelector: "$.DBInstances[0].DBInstanceStatus" DesiredValues: - "modifying" - name: WaitForDBInstanceStatusToAvailableAfterModify action: "aws:waitForAwsResourceProperty" timeoutSeconds: 600 isEnd: false description: | ## WaitForDBInstanceStatusToAvailableAfterModify Waits for the DB instance to change to an AVAILABLE state inputs: Service: rds Api: DescribeDBInstances DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PropertySelector: "$.DBInstances[0].DBInstanceStatus" DesiredValues: - "available" - name: VerifyDBInstancePubliclyAccess action: "aws:assertAwsResourceProperty" timeoutSeconds: 600 isEnd: true description: | ## VerifyDBInstancePubliclyAccess Confirms public accessibility is disabled on the DB instance. inputs: Service: rds Api: DescribeDBInstances DBInstanceIdentifier: "{{ GetRDSInstanceIdentifier.DbInstanceIdentifier }}" PropertySelector: "$.DBInstances[0].PubliclyAccessible" DesiredValues: - "False"
リソースの確認
展開されるリソースの確認をしていきます。Configが有効化されているリージョンで先ほどのCloudFormationテンプレートを使ってスタックを作成してください。
Configルール
rds-instance-public-access-check
というConfigルールが作成されています。
対象のリソース変更時にチェックが実行され、非準拠となった場合には修復アクションで定義したSSMドキュメント(ConfigRemediation-DisablePublicAccessToRDSInstance)が実行されます。
ここの修復アクションで使われる権限は、新規で作成しているIAMロールを指定しています。
このConfigルールではRDSのDBインスタンスを対象にチェックしており、パブリックアクセスが有効になっているものがあると非準拠となります。
SSMドキュメント
先ほど修復アクションで指定されていたSSMドキュメントを確認します。
AWS Systems Managerのコンソールから ドキュメント>自己所有 を確認すると、ConfigRemediation-DisablePublicAccessToRDSInstance
というSSMドキュメントが作成されています。
このドキュメントで実行されるステップを確認すると、以下の7つが定義されています。
ざっと簡単に説明すると、以下のようなことを各ステップで行なっています。
- DBインスタンスの情報を取得
- ステータスが
available
になるまで待機 - ステータスが
available
になったことを確認 - DBインスタンスの
public accessibility
を無効化 - ステータスが
modifying
になるまで待機 - ステータスが
available
になるまで待機 - DBインスタンスの
public accessibility
が無効化されていることを確認
上記を非準拠となったDBインスタンスに対して実行することで、パブリックアクセスが可能なものを自動修復する仕組みとなっています。
動作確認
それではパブリックアクセスが有効になっているDBインスタンスを作成して、どのように動作するのか確認してみましょう。
パブリックアクセスを「あり」にチェックを入れてDBインスタンスを作成してみます。
Configルールを確認すると、非準拠のリソースとして作成したDBインスタンスが表示されました。
そこから少し待つと、ステータスに「アクションが正常に実行されました」と表示されます。
改めて作成したDBインスタンスを確認してみると、パブリックアクセスが自動で修復されていることが確認できました。
なぜAWS管理のSSMドキュメントを使わないのか
AWSから提供されているSSMドキュメントにAWSConfigRemediation-DisablePublicAccessToRDSInstance
という今回作成したドキュメントに似たものがあります。(というよりこのドキュメントをもとに作成しました。)
このドキュメントを使わなかった理由は、DBインスタンスを作成時の修復を想定されたステップになっていなかったからです。
このSSMドキュメントを使用して修復アクションを実行してみた所、既存のRDSに対しては問題なく実行できるのですが、新規作成のDBインスタンスを実行した際にはエラーとなります。
コンソールからエラー詳細が確認できないので、CLIで確認すると以下のエラーメッセージが出力されました。
"Step fails when it is Execute/Cancelling action. Property value 'creating' from the API output is not in the desired values. Desired values: ['available'].. Please refer to Automation Service Troubleshooting Guide for more diagnosis details."
これは、DBインスタンスが作成中(creating)のステータス時にSSMドキュメントが ステータスがavailable
になったことを確認する のステップが実行されてしまったため、想定とステータスが違うことでエラーとなったようです。
そのため、今回はAWS管理のSSMドキュメントをもとに ステータスがavailable
になるまで待機するステップを追加したものを新規で作成しています。
おわりに
Configルールの自動修復を使ってRDSのパブリックアクセスを無効にしてみました。RDSのインスタンスをパブリックに公開するケースはほとんどないかと思いますので、統制をかけたい際にぜひ利用してみてください。